#include "stdafx.h"
#include "EssNetworkPCH.h"
#include "EssNetwork.h"
#include "EssBuffer.h"

EssNetwork::EssNetwork() : mWnd(0), mMessage(0)
{
	memset((void*)&mData, 0, sizeof(mData));
}

EssNetwork::~EssNetwork()
{

}

bool EssNetwork::initialize(HWND wnd, UINT message, WORD majorVersion, WORD minorVersion)
{
	if (WSAStartup(MAKEWORD(majorVersion, minorVersion), &mData) != NO_ERROR)
	{
		return false;
	}
	mWnd = wnd;
	mMessage = message;
	return true;
}

void EssNetwork::terminate()
{
	for (ConnectorIterator c = mConnectors.begin(); c != mConnectors.end(); ++c)
	{
		c->second->closeSock();
		delete c->second;
	}
	mConnectors.clear();

	for (AcceptorIterator a = mAcceptors.begin(); a != mAcceptors.end(); ++a)
	{
		a->second->closeSock();
		delete a->second;
	}
	mAcceptors.clear();

	WSACleanup();
}

EssAcceptor* EssNetwork::exeListen(const char* localIP, u_short port)
{
	EssAcceptor* acceptor = new EssAcceptor();

	if (!acceptor->createSock())
	{
		delete acceptor;
		return 0;
	}

	if(!acceptor->bindSock(localIP, port))
	{
		delete acceptor;
		return 0;
	}

	if (!acceptor->listenSock(mWnd, mMessage))
	{
		delete acceptor;
		return 0;
	}

	std::pair<AcceptorIterator, bool> res = mAcceptors.insert(AcceptorValue(acceptor->getSocket(), acceptor));
	assert(res.second && "An acceptor with the same socket already exists.");
	if (!res.second)
	{
		return 0;
	}
	return acceptor;
}

EssConnector* EssNetwork::exeConnect(const char* dest_ip, u_short dest_port)
{
	EssConnector* connector = new EssConnector();

	if (!connector->createSock())
	{
		delete connector;
		return 0;
	}

	if (!connector->connectSock(mWnd, mMessage, dest_ip, dest_port))
	{
		delete connector;
		return 0;
	}

	std::pair<ConnectorIterator, bool> res = mConnectors.insert(ConnectorValue(connector->getSocket(), connector));
	assert(res.second && "A connector with the same socket already exists.");
	if (!res.second)
	{
		return 0;
	}
	return connector;
}

void EssNetwork::removeAcceptor(const EssAcceptor* acceptor)
{
	AcceptorIterator a = mAcceptors.find(acceptor->getSocket());
	if (a != mAcceptors.end())
	{
		delete a->second;
		mAcceptors.erase(a);
	}
}

void EssNetwork::removeConnector(const EssConnector* connector)
{
	ConnectorIterator c = mConnectors.find(connector->getSocket());
	if (c != mConnectors.end())
	{
		delete c->second;
		mConnectors.erase(c);
	}
}

LRESULT EssNetwork::handleMessage(WPARAM wParam, LPARAM lParam)
{
	SOCKET socket = (SOCKET)wParam;
	long event = WSAGETSELECTEVENT(lParam);
	int error = WSAGETSELECTERROR(lParam);

	switch(event)
	{
	case FD_ACCEPT:
		{
			AcceptorIterator a = mAcceptors.find(socket);
			if (a != mAcceptors.end())
			{
				sockaddr_in addr;
				int len = sizeof(addr);
				SOCKET peer = accept(socket, (sockaddr*)&addr, &len);
				if (peer != INVALID_SOCKET)
				{
					EssConnector* connector = new EssConnector(peer);
					std::pair<ConnectorIterator, bool> res = mConnectors.insert(ConnectorValue(peer, connector));
					assert(res.second && "A connector with the same socket already exists");
					if (res.second)
					{
						if (WSAAsyncSelect(peer, mWnd, mMessage, FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR)
						{
							assert(0);
						} 
						else
						{
							onAccept(a->second, connector);
						}
					}
				}
			}
			else
			{
				assert(0 && "An unrecognized socket is involved.");
			}
		}
		break;
	case FD_CONNECT:
		{
			ConnectorIterator c = mConnectors.find(socket);
			if (c != mConnectors.end())
			{
				if (error == 0)
				{
					if (WSAAsyncSelect(socket, mWnd, mMessage, FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR)
					{
						assert(0);
					} 
					else
					{
						onConnect(c->second);
					}
				}
				else
				{
					onConnectFailed(c->second);
				}
			} 
			else
			{
				assert(0 && "An unrecognized socket is involved.");
			}
		}
		break;
	case FD_READ:
		{
			ConnectorIterator c = mConnectors.find(socket);
			if (c != mConnectors.end())
			{
				// An application need not read all available data in response to an FD_READ message -- a single recv in response to each FD_READ message is appropriate.
				// If an application issues multiple recv calls in response to a single FD_READ, it can receive multiple FD_READ messages. Such an application can require
				// disabling FD_READ messages before starting the recv calls by calling WSAAsyncSelect with the FD_READ event not set.
				if(WSAAsyncSelect(socket, mWnd, mMessage, FD_WRITE | FD_CLOSE) == SOCKET_ERROR)
				{
					assert(0);
				}

				if (c->second->recv() > 0)
				{
					if (onReceive(c->second))
					{
						c->second->getRecvBuffer().clear();
					}
				}

				if (WSAAsyncSelect(socket, mWnd, mMessage, FD_READ | FD_WRITE | FD_CLOSE) == SOCKET_ERROR)
				{
					assert(0);
				}
			} 
			else
			{
				assert(0 && "An unrecognized socket is involved.");
			}
		}
		break;
	case FD_WRITE:
		{
			ConnectorIterator c = mConnectors.find(socket);
			if (c != mConnectors.end())
			{
				c->second->_dispatchSendBuffers();
			}
			else
			{
				assert(0 && "An unrecognized socket is involved.");
			}
		}
		break;
	case FD_CLOSE:
		{
			ConnectorIterator c = mConnectors.find(socket);
			if (c != mConnectors.end())
			{
				onDisconnect(c->second);
			} 
			else
			{
				assert(0 && "An unrecognized socket is involved.");
			}
		}
		break;
	default:
		break;
	}

	return 0;
}